2023-05-10 estsoft - python - class, class attr and instance attr, magic methods, UserInfo and BookInfo 실습, inheritance

Class

구글콜랩 링크

Class attribute and Instance attribute

CPP의 static attribute???

클래스 변수는 클래스 바로 하위에 있는 변수이다. 클래스 변수는 모든 인스턴스가 공유할 수 있다. 따라서 {{class-name}}.{{class-attr}} 식으로 접근이 가능하다. 물론 self.{{class-attr}}도 가능함.

문제는.... class attr를 instance attr와 혼동이 생길 우려가 있다는 것이다. 다음 예를 들어보자.
우리는 별 생각 없이 Car.maxSpeed를 변경한 걸로 알겠지만, 사실 엄밀히 따지고 보면 change_speed 메서드 안에서 새로운 instance attr인 speed를 만들어 저장한 것이 된다. 이것이 문제가 되는 이유는, Car.maxSpeed는 300으로 그대로인데, 프로그래밍 할 시점엔 실수하기 매우 쉽다는 것이다. 따라서, class attr를 참조할 땐 반드시 self가 아니라 클래스명을 사용하도록 권장한다.

class Car:
	maxSpeed = 300
	maxPeople = 5

	def add_kinds(self, name):
		self.kinds.append(name)

	def change_speed(self, speed):
		self.speed = speed # 💥💥💥💥💥💥💥💥💥💥💥💥

k5 = Car()
k3 = Car()

k5.add_kinds('k5')
k3.add_kinds('k3')
k5.change_speed(500)
k3.change_speed(250)

class attr를 instance attr와 혼동하지 않고 사용하는 아래의 코드이다. User 인스턴스가 추가될 때마다 User.count가 증가하여 각자 고유한 id instance attr를 가지는 것을 볼 수 있다.

class User:
	count = 0

	def __init__(self):
		self.id = User.count
		User.count += 1

몹과 유저를 만들어 서로 공격하게 만들어보자.

class User(object):
    
    def __init__(self, 이름, 공격력, 체력, 마력, 크기_넓이, 크기_높이, x, y):
        self.name = 이름
        self.power = 공격력
        self.hp = 체력
        self.mp = 마력
        self.width = 크기_넓이
        self.height = 크기_높이
        self.x = x
        self.y = y
        self.alive = True
    
    def take_damage(self, other):
        if self.hp < 0:
            print(f'DEBUG ---> {self.name} has already died.')
            pass
        self.hp -= other.power
        print(f'DEBUG ---> {other.name} attacks {self.name}. {self.name}.hp is now {self.hp}')
        if self.hp < 0:
            self.alive = False
            print(f'DEBUG ---> {self.name} died by {other.name}')

    def attack(self, other):
        other.take_damage(self)

    

class Mob(object):
    def __init__(self, 이름, 공격력, 체력, 마력, 크기_넓이, 크기_높이, 아이템확률, x, y):
        self.name = 이름
        self.power = 공격력
        self.hp = 체력
        self.mp = 마력
        self.width = 크기_넓이
        self.height = 크기_높이
        self.dropRate = 아이템확률
        self.x = x
        self.y = y

    
    def __repr__(self):
        return f"""{{
    name: {self.name}        
    power: {self.power}
    hp: {self.hp}
    mp: {self.mp}
    width: {self.width}
    height: {self.height}
    dropRate: {self.dropRate}
    x: {self.x}
    y: {self.y}
}}
"""


    def attack(self, user):
        user.take_damage(self)

    def take_damage(self, other):
        if self.hp < 0:
            print(f'DEBUG ---> {self.name} has already died.')
            pass
        self.hp -= other.power
        print(f'DEBUG ---> {other.name} attacks {self.name}. {self.name}.hp is now {self.hp}')
        if self.hp < 0:
            self.alive = False
            print(f'DEBUG ---> {self.name} died by {other.name}')


class Slime(Mob):
	"""
	아직은 상속에 대하여 배우지 않았지만, 써보는 건 공짜잖아?
	"""

    def __add__(self, other):
        if isinstance(other, Slime):
            return Slime(f'왕{self.name + other.name}', self.power + other.power, 
                         self.hp + other.hp, 
                         self.mp, 
                         self.width + other.width,
                         self.height + other.height,
                         self.dropRate,
                         self.x,
                         self.y)
        

슬라임 = Slime('슬라임', 1, 10, 10, 2, 2, 100, 0, 0)
오크 = Mob('오크', 10, 10, 10, 2, 2, 80, 0, 0)
고블린 = Mob('고블린', 100, 10, 10, 2, 2, 60, 0, 0)
드래곤 = Mob('드래곤', 1000, 10, 10, 2, 2, 40, 0, 0)
해골 = Mob('해골', 10000, 10, 10, 2, 2, 1, 0, 0)

licat = User('licat', 9, 30, 15, 10, 10, 0, 0)
mura = User('mura', 10, 35, 0, 10, 10, 0, 0)

슬라임.attack(licat)
해골.attack(mura)

licat.attack(슬라임)


슬라임2 = Slime('슬라임', 1, 10, 10, 2,2,100, 0,0)
왕슬라임 = 슬라임 + 슬라임2

print(왕슬라임)

아래 예제는 클래스를 활용하여 간편하게 html 문서를 만드는 저시기를 설명한다.

# 쉽고 중요한 예제!

class BlogFactory(object):
    dataset = []

    def __init__(self, 제목, 내용, 조회수, 글쓴이, 생성날짜):
        self.title = 제목
        self.content = 내용
        self.count = 조회수
        self.writer = 글쓴이
        self.create_date = 생성날짜
        BlogFactory.dataset.append(self)

게시글1 = BlogFactory(
    '오늘 제주의 날씨',
    '오늘 제주의 날씨는 참 좋네요! 블라블라',
    '0',
    '이호준',
    '2023/05/10',
    )

게시글2 = BlogFactory(
        '오늘 부산의 날씨',
        '오늘 부산의 날씨는 참 좋네요! 블라블라',
        '1000000',
        '김재현',
        '2023/05/10',
    )

게시글3 = BlogFactory(
        '오늘 강원의 날씨',
        '오늘 강원의 날씨는 참 좋네요! 블라블라',
        '10000',
        '범남궁',
        '2023/05/10',
    )
 

for i in BlogFactory.dataset:
    print(f'<h2>{i.title}</h2>')
    print(f'\t<p>{i.content}</p>')
    print(f'\t<p>{i.writer}</p>')
    print(f'\t<p>{i.count}</p>')
    print(f'\t<p>{i.create_date}</p>')

Magic methods

magic methods (python)

UserInfo, BookInfo 실습

repr, dir, vars, pprint.pformat + UserInfo and BookInfo 실습 (python)

Inheritance

python의 상속체계는 여타 언어들의 상속과 비슷해 보인다.

class Car(object):
    maxSpeed = 300
    maxPeople = 5

    def __init__(self):
        self.option = ['aircon', 'fly', 'wheel', 'nuc']

    def move(self, x):
        print(x, '의 스피드로 달리고 있습니다.')
    def stop(self):
        print('멈췄습니다.')

class HybridCar(Car):
    battery = 1000
    batteryKM = 300

class ElectricCar(HybridCar):
    battery = 2000
    batteryKM = 600

# print(set(dir(Car)) - set(dir(object)))

assert id(Car.maxSpeed) == id(HybridCar.maxSpeed)  # ok
assert id(Car.move) == id(HybridCar.move)  # ok
bongbong = HybridCar()
cybertruck = ElectricCar()
assert id(bongbong.option) != id(cybertruck.option)